---
title: "Backend-driven UI"
---

Framework facilitates backend-initiated user interface modifications. These changes are made possible through **Code-Managed Components** (CMCs), distinct from the *Builder-Managed Components* (BMCs). 

CMCs, unlike BMCs, are dynamically created and modified via back-end code, and cannot be edited (but still can be viewed) within the application builder. It's important to also note that CMCs do not persist in your application's `ui.json` file and exist only during the application runtime, supporting dynamic UI adjustments.

<Note> 
To summarise:

**CMC** – Code-Managed Component 
- created via **application back-end**;
- **cannot be edited** in builder;
- is **not saved** to `ui.json`.

**BMC** – Builder-Managed Component 
- created via **builder**; 
- **can be edited** in builder;
- is **saved** to `ui.json`.
</Note>

## UI manager

Framework provides two independent approaches for managing your application's UI: initializing a base UI and making session-specific updates.

### Initializing base UI

The `init_ui()` method sets up a UI manager to configure UI components at the application's startup. This creates a component set that is accessible across all sessions:

```python
import writer as wf

with wf.init_ui() as ui:
    with ui.Page(id="my-page"):
        ui.Header({"text": "Hello World!"})
        ui.ColumnContainer(id="column-container")
```

### Making session-specific updates

For dynamic, session-specific UI updates, the `ui` parameter is used within handler functions. This approach allows for real-time modifications tailored to individual user sessions:

```python
def display_user_data(ui, state):
    with ui.find("column-container"):
        with ui.Column():
            ui.Text({"text": f"And welcome {state["username"]}!"})
        with ui.Column():
            ui.Text({"text": f"Your data: {state["user_data"]}"})
```

## UI manager methods

### `find` method

You can use the `ui.find(component_id: str)` method to access existing components by ID:
```python
with ui.find("column-container"):
    with ui.Column():
        ...
```
If the component couldn't be found, the method raises a `RuntimeError`.

### `refresh_with` method

You can use the `ui.refresh_with(component_id: str)` method to replace children CMCs of an existing component (referenced by its ID):
```python
with ui.refresh_with("my-page"):
    # Previously existing children are cleared
    ui.Header({"text": "Hello New World!"})
    with ui.ColumnContainer():
        with ui.Column():
            ui.Text({"text": "Nobody here for now..."})
```

This method also allows to clear children CMCs of a component:
```python
with ui.refresh_with("my-page"):
    # Empties the page
    pass
```

If a targeted component has builder-managed children, they will not be removed. A warning message will be recorded in the application's log for each BMC attempted to be removed. This does not stop the execution of the method – any remaining CMCs will still be removed.
As well as with `find` method, it also raises a `RuntimeError` if it fails to find a referenced component.

### `parent` method

`ui.parent(component_id: str, level: int = 1)` gives access to the id to parents at higher levels.

```python
container = ui.parent('my-text') # first parent id

container = ui.parent('my-text', 3) # level 3 parent id
with ui.find(container):
	...
```

### Component methods

UI manager contains methods linked to each front-end component. For example, in previous code snippets we provide a `ui.Text` method, which is used for creating [Text components](https://www.streamsync.cloud/component-list.html#text).

This method expects `content: dict` as first argument, which enables you to set the field properties of the component, through corresponding keys:
```python
ui.Text(
    {
        "text": "Hello World!",  
        # The text content of the component
        "useMarkdown": "no",  
        # Will not use Markdown
        "alignment": "left",  
        # Text is aligned to the left
        "primaryTextColor": "#000000",  
        # The text color is black
        "cssClasses": "my-text hello-world"  
        # Apply 'my-text' and 'hello-world' CSS classes
    }
)
```

In a similar way, every other component method also expects `content` as its first argument:
```python
ui.VideoPlayer(
    {
        "src": "https://example.com/assets/mov/rick-roll-video.mov",
        "autoplay": "yes",
        "controls": "no",
        "muted": "no",
        "loop": "no",
    }
)
```

In addition to `content`, a set of fields which is specific to the component type, you can also modify the base properties of the component itself, which are:
- **`id: str`**: A unique identifier used for accessing the component after it was created.  
    *Providing an identifier that is already taken would result in `RuntimeWarning` and the existing component being overwritten with a newly created one.*
    ```python
    ui.Text(
        {"text": "Hello World!"}, 
        id="hello-world-text"
        )
    ```
    *If no ID is provided with a component, a UUID is automatically generated for it.*  
    <Note> 
    Make sure to provide an `id` if you intend to `find` the component later  
    As the `find` method relies on `id` of the component, retrieval might get tricky if its `id` was generated randomly.
    </Note>
- **`position: int`**: Determines the display order of the component in relation to its siblings.  
    Position `0` means that the component is the first child of its parent.  
    Position `-2` is used for components – such as [sidebars](https://www.streamsync.cloud/component-list.html#sidebar) – that have a specific reserved position not related to their siblings.  
    ```python
    ui.Text(
        {"text": "Hello Parent, I'm your first child!"}, 
        position=0
        )
    ```
    *Position is calculated automatically for each component, and you should be careful when you override it with predefined value, as this might lead to unexpected results.*
- **`parentId: str`**: Determines the parent [container](#container-components) for the component. By default, components recognise the container in the context of which they were defined as their parent. This allows for linking components to their parents outside of context, or for overriding a parent within a context.
    ```python
    ui.Text(
        {"text": "Hello Parent, I'm your child too!"}, 
        parentId="dear-parent"
        )
    ```
- **`visible: bool | str`**: Determines the visibility of the component, `True` by default.
    ```python
    ui.Text({"text": "I'm visible!"}, visible=True)

    ui.Text({"text": "And I'm not!"}, visible=False)

    ui.Text({"text": "My visibility depends on the @{my_var}!"}, visible="my_var")
    ```
- **`handlers: dict[str, callable]`**: Attaches [event handlers](https://www.streamsync.cloud/event-handlers.html) to the component. Each dictionary key represents an event, and its value is the corresponding handler.:
    ```python
    def increment(state):
        state["counter"] += 1

    initial_state = wf.init_state({"counter": 0})

    ...
    
    ui.Button(
        {"text": "My Counter: @{counter}"}, 
        handlers={"wf-click": increment}
        )
    # You have two options for adding a function 
    # to the `handlers` dictionary: 
    # directly pass the function itself, 
    # or use the function's name as a string. 
    # Both approaches yield the same outcome.
    ```
    *A component can be linked to multiple event handlers.*
- **`binding: dict[str, str]`**: Links the component to a state variable via [binding](https://www.streamsync.cloud/builder-basics.html#binding). The dictionary key is the bindable event, and the value is the state variable's name:
    ```python
    initial_state = wf.init_state({
        "header_text": "Default Text"
        "counter": 0
        })
    
    ...

    ui.TextInput(
        {"label": "Bound Text"}, 
        binding={"wf-change": "header_text"}
        )
    # This input will display "Default Text"
    # Changing the text in this input will modify the `header_text` variable

    ui.SliderInput(
        {"minValue": 0, "maxValue": 300, "stepSize": 1}, 
        binding={"wf-number-change": "counter"}
        )
    # This slider will have 0 as a default value
    # Sliding it will modify the `counter` variable
    ```
    *Unlike handlers, a component can be linked to just one variable via a bindable event. If the `binding` dictionary includes multiple event-variable pairs, a `RuntimeError` will be triggered.*

### Container components

Framework provides multiple layout components that can serve as *containers* for other components. 

You can use `with` keyword to define such layouts:
```python
with ui.Section({"title": "My Section"}):
    ui.Text({"text": 'Hello World!'}, id="hello-world")
```

It also allows for "chaining" multiple containers together, creating extensive and deeply-nested layout structures when needed:
```python
with ui.ColumnContainer(id="cmc-column-container"):
    with ui.Column(id="cmc-column-1"):
        with ui.Section({"title": "My Section 1"}):
            ui.Text({"text": 'Hello World!'}, id="hello-world-1")
    with ui.Column(id="cmc-column-2"):
        with ui.Section({"title": "My Section 2"}):
            ui.Text({"text": 'Hello World again!'}, id="hello-world-2")
```

<Warning> 
Most components depend on being inside of a container. This means, for example, that Text components in code above cannot be created as "orphans", outside a Column or Section. Attempting to do so would raise an `UIError`.
</Warning>

By default, components inside container's `with` are being *appended* to it:
```python
with ui.Column(id="cmc-column-1"):
    ui.Text({"text": 'Hello World!'}, id="hello-world-1")

...

# Retrieves the Column component created before
with ui.find(id="cmc-column-1"): 
    # The following component is going to be appended 
    # to the retrieved Column
    ui.Text({"text": 'Hello World again!'}, id="hello-world-2")
```

This will result in a Column component having two children Text components. To replace or clear the children, use [`refresh_with` method](#refresh_with-method):

```python
with ui.Column(id="cmc-column-1"):
    ui.Text({"text": 'Hello World!'}, id="hello-world-1")

...

with ui.refresh_with(id="cmc-column-1"):
    # The following component is going to replace 
    # previously existing children of the retrieved Column
    ui.Text(
        {"text": 'To Hello World, or not to Hello World?'}, 
        id="hello-world-new"
        )
```